﻿#region Using directives

using System;
using System.Threading;
using System.Collections.Generic;
using System.Text;

using Weazel.Toad.Application.Items;

using Weazel.Gezel.Parser;
using Weazel.Gezel.Model;
using Weazel.Gezel.Model.Errors;


#endregion

namespace Weazel.Toad.Application
{
  public class GezelModelAnalysis
  {
    public event EventHandler<EventArgs> ParsingSucceeded;
    public event EventHandler<EventArgs> ParsingCompleted;

    // must be object in order to
    // have compiler understand call
    // to Interlocked.Exchange()
    private object description;
    
		private Thread parsingThread;

    private GezelTextFile gezelTextFile;
		public GezelTextFile GezelTextFile
		{
			get { return gezelTextFile; }
		}

    private List<ParseError> parseErrors;
    public IList<ParseError> ParseErrors
    {
      get { return parseErrors.AsReadOnly(); }
    }

    private List<VerificationError> verificationErrors;
    public IList<VerificationError> VerificationErrors
    {
      get { return verificationErrors.AsReadOnly(); }
    }

		private List<VerificationWarning> warnings;
		public IList<VerificationWarning> Warnings
		{
			get { return warnings.AsReadOnly(); }
		}

    private Model gezelModel;
    public Model GezelModel
    {
      get { return gezelModel; }
    }

    public GezelModelAnalysis()
    {
      this.parseErrors = new List<ParseError>();
      this.verificationErrors = new List<VerificationError>();

      Application.Instance.SelectedItemChanged +=
        new EventHandler<SelectedItemChangedEventArgs>(Application_SelectedItemChanged);

      parsingThread = new Thread(new ThreadStart(parse));
      
      // Setting the thread to run as a background thread
      // means that it gets automatically stopped when the
      // foreground thread(s) of the application stops
      // and thus we dont have to terminate it manually.
      parsingThread.IsBackground = true;
      parsingThread.Priority = ThreadPriority.Lowest;
      parsingThread.Start();
    }

    private void parse()
    {
      try
      {
        while (true)
        {
					string fileName = gezelTextFile != null ? gezelTextFile.DisplayName : null;

          // if we dont read the description and set it to null
          // atomically we could potentially recieve an update
          // on the main thread just before we set it to null
          // and thus miss an update
          string originalDescription = (string)Interlocked.Exchange(ref this.description, null);

          if (originalDescription != null && fileName != null)
          {
            // clear previous errors
            parseErrors.Clear();
            verificationErrors.Clear();
						try
						{
							Model model = ParseModel.Parse(ref parseErrors, originalDescription, fileName);

							if (parseErrors.Count == 0)
							{
								model.Verify(ref verificationErrors, ref warnings);
							}

							if (ParsingCompleted != null)
							{
								ParsingCompleted(this, EventArgs.Empty);
							}

							if (parseErrors.Count == 0 /* && verificationErrors.Count  == 0 */)
							{
								gezelModel = model;
								if (ParsingSucceeded != null)
									ParsingSucceeded(this, EventArgs.Empty);
							}
						}
						catch (ParseException e)
						{
							// ignore certain types of exceptions (non-terminated strings etc)             
						}
						catch (ThreadAbortException)
						{
							// ok, thread aborted
							return;
						}
						catch (ThreadInterruptedException)
						{
							// ok, thread interrupted
							return;
						}
						catch (Exception e)
						{
							// check settings to see if we should show this error to the user
							if(!Weazel.Toad.Application.Configuration.Settings.Default.doNotShowParserExceptions)
								Forms.ExceptionForm.Show(e);
						}
          }

          // no matter what, we
          // wait one second 
          Thread.Sleep(1000);            
        }
      }
      catch (ThreadAbortException)
      {
        // ok, thread aborted
      }
      catch (ThreadInterruptedException)
      {
        // ok, thread interrupted
      }
    }

    void Application_SelectedItemChanged(object sender, SelectedItemChangedEventArgs e)
    {
      // first unhook previous item
      if (gezelTextFile != null)
      {
        gezelTextFile.ItemContentsChanged -= new EventHandler<EventArgs>(gezelTextFile_ItemContentsChanged);
        gezelTextFile.ItemChanged -= new EventHandler<EventArgs>(gezelTextFile_ItemChanged);
      }

      // if new selected item is gezel model
      // hook up new item
      ItemBase item = Application.Instance.SelectedItem;

      if (item != null && item is GezelTextFile)
      {
        gezelTextFile = item as GezelTextFile;
        description = gezelTextFile.Contents;
        gezelTextFile.ItemContentsChanged += new EventHandler<EventArgs>(gezelTextFile_ItemContentsChanged);
        gezelTextFile.ItemChanged += new EventHandler<EventArgs>(gezelTextFile_ItemChanged);
      }
      else // otherwise release reference to previous gezel model
      {
        gezelTextFile = null;
      }
    }

    /// <summary>
    /// Gezel model changed, stor
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void gezelTextFile_ItemContentsChanged(object sender, EventArgs e)
    {
      description = gezelTextFile.Contents;
    }

    void gezelTextFile_ItemChanged(object sender, EventArgs e)
    {
      description = gezelTextFile.Contents;
    }
  }
}
